<template>
  <div class="center-map">
    <div id="map" ref="map" class="mapContainer"></div>

    <!-- <div class="brother"></div> -->

    <div class="sister"></div>

    <div class="decorate"></div>
  </div>
</template>

<script>
import Vue from 'vue'
import styles, { icons } from './layerStyle'
import {
  Marker,
  LineString,
  VectorLayer,
  ui,
  ImageLayer,
  Extent,
  animation,
  Polygon,
  MultiPolygon
} from 'maptalks'
import * as mapTalkUtils from '@gistool/maptalks'
import {
  union,
  getGeom,
  truncate,
  length as turfLength,
  polygon as turfPolygon,
  multiPolygon as turfMultiPolygon,
  simplify as turfSimplify,
  intersect as turfMask
} from '@turf/turf'
import { parseTime } from '@/utils/tools.js'
import { mapState } from 'vuex'
import {
  citys,
  cityMap,
  cityCenters,
  cityReverMap,
  cityCenterFromWeather
} from './cityList'
import { debounce } from 'lodash'

const publicPrefix = './map/geojson'
const regionTypeEnum = {
  guangdong: 'guangdong',
  china: 'china'
}

export default {
  mixins: [],
  components: {},
  data() {
    return {
      //选中的区域
      currentArea: '广东省',
      //悬浮的区域
      hoverArea: '广东省',
      regionMinZoom: 3,
      regionMaxZoom: 11
    }
  },
  computed: {
    ...mapState({}),
    routeName() {
      return this.$route.name
    }
  },
  watch: {
    scaleRatio: {
      handler(val) {
        //校正鼠标事件
        this.$map && this.$map.checkSize()
      },
      immediate: true
    },
    currentArea(nval, oval) {},
    hoverArea(nval, oval) {}
  },
  beforeCreate() {
    this.mapFinished = new Promise((resolve) => {
      this.isMapFinished = resolve
    })
  },
  mounted() {
    Vue.isMapLoadingFinshed = false
    this.$nextTick(async () => {
      await this.initMap()
      //TODO:添加默认加载的geojson图层
      await this.addLayerList() //添加预加载图层
      //TODO:添加层层叠叠的效果样式
      await this.addEffectLayer(regionTypeEnum.guangdong, {
        //设置minZoom和maxZoom后下钻市/区县不需要额外去控制效果图层的显隐
        minZoom: this.regionMinZoom,
        maxZoom: this.regionMaxZoom,
        visible: true,
        hitDetect: false
      })
      Vue.isMapLoadingFinshed = true
      this.isMapFinished()
    })
  },
  beforeDestroy() {
    Vue.isMapLoadingFinshed = true
    this.mapFinished = null
    if (!this.$map) return
    this.$map.off('click', this.mapOnClick)
    this.$map.off('moveend', this.mapOnMoveend)
    this.$map.remove()
    this.$map = null
    window.mapTalk = null
    // 重置vuex状态
    this.$store.dispatch(`${this.routeName}/setCurrentCity`, '广东省')
  },
  methods: {
    getList() {
      this.mapFinished.then(() => {
        this.getData()
      })
    },
    getData() {
      //TODO:地图初始化之后从接口获取数据
    },
    async initMap() {
      const mapOptions = this.getMapOption()
      var map = await mapTalkUtils.initMap(this.$refs.map, {
        ...mapOptions
      })
      map.checkSize()
      this.$map = map
      //记录初始范围
      this.$extent = map.getExtent()
      map.$originView = map.getView()

      this.$map.on('click', this.mapOnClick)
      this.$map.on('mousemove', this.mapMove)
      window.mapTalk = map
      this.initInfoWindow()
    },
    async waitMapFinish() {
      if (!this.mapFinished) return
      await this.mapFinished
    },
    getMapOption() {
      //TODO:默认是广东省的地图相机options,需要根据项目需求调整
      const { vec_mercator, img_mercator } = mapTalkUtils.getTdtLayers({
        vec_mercator:
          'grayscale(1) saturate(1) sepia(1) contrast(1) hue-rotate(160deg) brightness(0.6) blur(0px) hue-rotate(350deg) saturate(2.5)', //invert(0.8)  saturate(0.8) sepia(0.8) contrast(1.9) hue-rotate(182deg)
        img_mercator:
          'grayscale(1) saturate(1) sepia(1) contrast(1) hue-rotate(160deg) brightness(0.6) blur(0px) hue-rotate(350deg) saturate(2.5)', //invert(0.8)  saturate(0.8) sepia(0.8) contrast(1.9) hue-rotate(182deg)
        cva_mercator: 'hue-rotate(130deg) '
      })
      const mapOptions = {
        spatialReference: 'EPSG:3857',
        center: [113.70460354248598, 23.01514368872145],
        layers: [img_mercator],
        zoomAnimation: false,
        panAnimation: false,
        rotateAnimation: false,
        zoom: 7.72,
        maxZoom: 19,
        minZoom: 0 //this.isShowChina ? 0 : this.regionZoom
      }
      return mapOptions
    },
    //添加预加载图层
    async addLayerList() {
      const layerList = [
        {
          url: './map/geojson/gd_main_outline.geojson',
          layerName: 'map-material-line',
          visible: true,
          style: styles['map-bg'],
          zIndex: 8
        },
        {
          url: './map/geojson/guangdong_county.geojson',
          layerName: 'county',
          visible: false,
          style: styles['county'],
          zIndex: 11
        }
      ]
      await mapTalkUtils.loadGeojsons(this.$map, layerList)
    },
    //图层初始化时的层叠样式
    addEffectLayer(layerName, opt = {}, prefix = 'geojson') {
      return new Promise((resolve, reject) => {
        const layerInfo = [
          {
            url: `${publicPrefix}/${layerName}.${prefix}`,
            layerName: layerName,
            style: styles['county'],
            zIndex: 20,
            ...opt
          }
        ]
        const offsetList = {
          china: 1
        }
        mapTalkUtils
          .loadGeojsons(this.$map, layerInfo)
          .then((res) => {
            const { mainGeoList } = this.getUnionOutline(layerName) || {}
            if (!mainGeoList) {
              resolve(res[0])
              return
            }
            const topLayer = new VectorLayer(
              layerName + '-effect-material',
              mainGeoList,
              {
                style:
                  styles[layerName + '-map-material'] || styles['map-material'],
                zIndex: 4,
                ...opt
              }
            ).addTo(this.$map)
            new VectorLayer(layerName + '-effect-bg', mainGeoList, {
              style: styles[layerName + '-map-bg'] || styles['map-bg'],
              zIndex: 5,
              ...opt
            }).addTo(this.$map)
            //根据缩放层级计算阴影图层偏移量
            const [firstLayer, secondLayer] = this.addEffectOffsetLayer(
              topLayer,
              offsetList[layerName],
              opt
            )
            this.$maskFeatuers = res[0].getGeometries()
            resolve(res[0])
          })
          .catch((err) => {
            console.warn(err)
            reject(err)
          })
      })
    },
    //根据缩放层级计算阴影图层偏移量
    addEffectOffsetLayer(layer, offset, layerOpt) {
      //根据缩放层级计算阴影图层偏移量
      const fitZoom = this.$map.getFitZoom(layer.getExtent(), false)
      const offsetY =
        Math.max(18 - fitZoom * 1.4, 1) / Math.pow(fitZoom, 2) + (offset || 0)
      const offsetX = offsetY * 0.4
      return this.addOffsetLayer(
        layer,
        { x: -offsetX, y: -offsetY },
        {
          ...layerOpt,
          maxZoom: fitZoom + 1
        }
      )
    },
    getUnionOutline(layerName, simplifyOpt = {}) {
      try {
        if (!this.$map) return
        const layer = this.$map.getLayer(layerName)
        if (!layer) return
        //合并轮廓
        let outline = turfPolygon([])
        layer.getGeometries().map((v) => {
          const geom = v.toGeoJSONGeometry().coordinates
          let polygon =
            v.type === 'Polygon' ? turfPolygon(geom) : turfMultiPolygon(geom)
          //简化轮廓
          turfSimplify(polygon, {
            tolerance: 0.003,
            highQuality: true,
            mutate: true,
            ...simplifyOpt
          })
          //turf合并的算法对浮点型计算精度有要求，只需要保留6位小数的坐标
          truncate(polygon, {
            precision: 6,
            coordinates: 2,
            mutate: true
          })
          outline = union(polygon, outline)
        })

        let maxGeo = {}
        //多个要素的情况下 遍历求出最大周长
        if (outline.geometry.type === 'MultiPolygon') {
          maxGeo = getGeom(outline).coordinates.reduce(
            (previousValue, currentCoord) => {
              let { mainGeoList } = previousValue
              let coord = [currentCoord[0]]
              let polygon = turfPolygon(coord)
              let currLength = turfLength(polygon)
              if (currLength > 60) {
                mainGeoList.push(coord)
              }
              return { mainGeoList }
            },
            { mainGeoList: [] }
          )
        } else {
          //找出主体轮廓 只取每个polygon的第一个ring，因为后面描述的都是holes
          //只有单个要素的情况下
          let coord = [[getGeom(outline).coordinates[0]]]
          maxGeo = {
            mainGeo: coord
          }
        }
        const mainGeoList = maxGeo.mainGeoList
          ? turfMultiPolygon(maxGeo.mainGeoList)
          : turfMultiPolygon(maxGeo.mainGeo)
        return { mainGeoList }
      } catch (err) {
        console.log(err)
        return
      }
    },
    //#endregion
    addOffsetLayer(layer, offset = { x: -0.05, y: -0.12 }, layerOpt = {}) {
      if (!this.$map) return
      let first = mapTalkUtils.offsetLayer(layer, offset)
      let second = mapTalkUtils.offsetLayer(first, {
        x: offset.x / 2,
        y: offset.y / 2
      })
      first.setStyle(styles['countyOffset'])
      second.setStyle(styles['countyOffsetBottom'])
      first.config({
        hitDetect: false,
        ...layerOpt
      })
      second.config({
        hitDetect: false,
        ...layerOpt
      })
      this.$map.addLayer(second)
      this.$map.addLayer(first)
      return [first, second]
    },
    addEffectFeature(adcode) {
      const cityLayer = this.$map.getLayer('guangdong')
      const cfeatures = cityLayer.getGeometries().filter((v) => {
        return v.getProperties().adcode == adcode
      })
      this.$maskFeatuers = cfeatures
      const features = cityLayer.filter(
        (v) => v.getProperties()['adcode'] === adcode
      )

      let layer = this.$map.getLayer('effect-feature')
      if (!layer) {
        layer = new VectorLayer('effect-feature', {
          maxZoom: 17.5
        })
        this.$map.addLayer(layer)
      }
      layer.show()
      layer.clear()
      features.forEach((feature) => {
        const materialF = feature.copy()
        materialF.setSymbol(styles['map-material'].symbol)
        const bgF = feature.copy()
        bgF.setSymbol(styles['map-bg'].symbol)
        const first = feature.copy().translate(0.01, -0.01)
        const second = feature.copy().translate(0.02, -0.02)
        first.setSymbol(styles['countyOffset'].symbol)
        second.setSymbol(styles['countyOffsetBottom'].symbol)
        layer.addGeometry([second, first, materialF, bgF])
      })
    },
    //#region 地图鼠标交互处理
    mapMove(e) {
      const feature = this.mouseMoveOnLayer(e)
      if (!feature) {
        this.hoverArea = '广东省'
        return
      }
      const layerName = feature.getLayer().getId()
      const coordinate = feature.getCoordinates()
      const featureProperties = feature.getProperties()
      const center = feature.getCenter()
      switch (layerName) {
        default:
          console.log(layerName, coordinate, featureProperties, center)
          break
        case 'guangdong':
          this.hoverArea = featureProperties['cityName']
          break
        case 'county':
          this.hoverArea = featureProperties['labelName']
          break
      }
    },
    mapOnClick(e) {
      const feature = this.mouseOnLayer(e)
      if (!feature) {
        this.currentArea = '广东省'
        return
      }
      const layerName = feature.getLayer().getId()
      const coordinate = feature.getCoordinates()
      const featureProperties = feature.getProperties()
      switch (layerName) {
        default:
          console.log(layerName, coordinate, featureProperties, center)
          break
        case 'guangdong':
          this.currentArea = featureProperties['cityName']
          break
        case 'county':
          this.currentArea = featureProperties['labelName']
          break
      }
    },
    mouseOnLayer(e) {
      let feature = undefined
      const layers = ['guangdong', 'county']
      this.$map.identify(
        {
          coordinate: e.coordinate,
          layers: [...layers]
        },
        (geos) => {
          if (geos.length === 0) {
            return
          }
          feature = geos[0]
          return feature
        }
      )
      return feature
    },
    mouseMoveOnLayer(e) {
      let feature = undefined
      const layers = ['guangdong', 'county']
      this.$map.identify(
        {
          coordinate: e.coordinate,
          layers: [...layers]
        },
        (geos) => {
          if (geos.length === 0) {
            return
          }
          feature = geos[0]
          return feature
        }
      )
      return feature
    },
    initInfoWindow() {
      //TODO:添加地图内部弹窗
    },
    handleLayerVisible(city, oval) {
      //进入地市添加层叠效果图层'effect-feature'
      if (!this.$map) return
      const county_layer = this.$map.getLayer('county')
      const effect_layer = this.$map.getLayer('effect-feature')
      if (city != '' && city != '广东省') {
        county_layer.show()
        county_layer.getGeometries().forEach((g) => {
          if (cityReverMap[g.properties.parent['adcode']] != city) {
            g.hide()
          } else {
            g.show()
          }
        })
        this.addEffectFeature(String(cityMap[city]))
      } else {
        county_layer && county_layer.hide()
        effect_layer && effect_layer.clear()
      }
    },
    zoomToCity(city, oCity) {
      if (!this.$map) return
      this.$nextTick(() => {
        if (city === '中国') {
          this.$map.setCenterAndZoom([102.641085592052, 36.89636846930544], 5)
          return
        }
        if (city === '广东省' || city === '') {
          this.$map.setCenterAndZoom(
            [113.49808631153958, 22.993568305081823],
            7.8
          )
        } else {
          let layer = null,
            attriName = null
          if (oCity === '广东省') {
            layer = this.$map.getLayer('guangdong')
            attriName = 'nameText'
          } else {
            if (!this.isCounty) {
              layer = this.$map.getLayer('guangdong')
              attriName = 'nameText'
            } else {
              layer = this.$map.getLayer('county')
              attriName = 'name'
            }
          }
          if (layer) {
            let geometries = layer.getGeometries()
            let results = null
            geometries.forEach((g) => {
              if (g.properties[attriName].indexOf(city) > -1) {
                if (!results) {
                  results = g.getExtent()
                } else {
                  results = results.combine(g.getExtent())
                }
              }
            })
            this.$map.fitExtent(results, 0.3)
          }
        }
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.center-map {
  --map-bg-color: #070f2e;
  --map-bg-secondary-color: rgb(5 49 48);
  position: relative;
  height: 100%;
  width: 100%;
  z-index: 0;

  ::v-deep .map-box-chart {
    pointer-events: auto !important;
  }

  .mapContainer {
    height: 100%;
    width: 100%;
    padding: 0px;
    margin: 0px;
  }

  //地图蒙层遮罩等
  .brother {
    position: absolute;
    height: 100%;
    width: 100%;
    top: 0px;
    left: 0px;
    background-image: linear-gradient(
      to right,
      var(--map-bg-color) 1%,
      var(--map-bg-secondary-color) 7%,
      transparent 36%,
      transparent 60%,
      var(--map-bg-secondary-color) 95%,
      var(--map-bg-color) 99%
    );
    pointer-events: none;
  }

  .sister {
    position: absolute;
    height: 100%;
    width: 100%;
    top: 0px;
    left: 0px;
    background-image: radial-gradient(
      50% 80%,
      transparent 26%,
      var(--map-bg-color)
    );
    pointer-events: none;
  }

  .decorate {
    position: absolute;
    // left: 996px;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    top: 175px;
    width: 100%;
    height: 714px;
    pointer-events: none;
    background-image: url('~@/assets/imgs/map/map-bg/decorate.png');
  }
}
</style>

